package com.tang.algorithm.multithreading;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author tlm
 * @version 1.0
 * @date 2020/4/20 9:52
 * 交替打印
 * 我们提供一个类：
 * <p>
 * class FooBar {
 * public void foo() {
 *     for (int i = 0; i < n; i++) {
 *       print("foo");
 *     }
 * }
 * <p>
 * public void bar() {
 *     for (int i = 0; i < n; i++) {
 *       print("bar");
 *     }
 * }
 * }
 * 两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法，另一个线程将会调用 bar() 方法。
 * <p>
 * 请设计修改程序，以确保 "foobar" 被输出 n 次。
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/print-foobar-alternately
 */
public class AlternatePrinting {
    public static void main(String[] args) throws InterruptedException {
//        FooBar fooBar = new FooBar(1);
//        fooBar.foo();
//        fooBar.bar();
    }
}

class FooBar {
    private int n;
    //标识变量，true执行foo,false执行bar
    private boolean flag = true;
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();

    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            lock.lock();
            try {
                while (!flag) {
                    //如果flag不为true,等待
                    condition1.await();
                }
                printFoo.run();
                flag = false;
                //通知condition2，可以继续
                condition2.signal();

            } finally {
                lock.unlock();
            }
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            lock.lock();
            try {
                while (flag) {
                    //如果flag为true，等待
                    condition2.await();
                }
                printBar.run();
                flag = true;
                //通知condition1，可以继续
                condition1.signal();

            } finally {
                lock.unlock();
            }
        }
    }
}